#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "ThreadPool.hpp"
#include "Task.hpp"

#define DEFUALT 8081
#define BACK_LOG 5

class TcpServer{
  private:
    int listen_sockfd;
    uint16_t port;
    ThreadPool<Task>* tp;
  public:
    TcpServer(uint16_t _port = DEFUALT):listen_sockfd(-1), port(_port), tp(nullptr)
    {}

    bool InitTcpServer()
    {
      tp = new ThreadPool<Task>();
      tp->InitThreadPool();

      listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
      if(listen_sockfd == -1){
        std::cerr << "socket error" << std::endl;
        return false;
      }

      struct sockaddr_in local;
      memset(&local, 0, sizeof(local));
      local.sin_family = AF_INET;
      local.sin_port = htons(port);
      local.sin_addr.s_addr = INADDR_ANY;
      socklen_t len = sizeof(local);
      if(bind(listen_sockfd, (sockaddr*)&local, len)){
        std::cerr << "bind error" << std::endl;
        return false;
      }
      
      if(listen(listen_sockfd, BACK_LOG)){
        std::cerr << "listen error" << std::endl;
        return false;
      }
  
      std::cout << "listen succeed..." << std::endl;

      return true;
    }

    void Loop()
    {
      struct sockaddr_in peer;
      while(true){
        socklen_t len = sizeof(peer);
        int sockfd = accept(listen_sockfd, (sockaddr*)&peer, &len); 
        if(sockfd == -1){
          std::cerr << "accept error" << std::endl;
          continue;
        }

        std::string ip = inet_ntoa(peer.sin_addr);
        int port = ntohs(peer.sin_port);
        std::cout << "get a new link" << "[" << ip << "]" << ": " << port << std::endl;
        Task t(ip, port, sockfd);
        tp->Push(t); 
      }
    }

    ~TcpServer()
    {
      if(listen_sockfd > 0) close(listen_sockfd);
      delete tp;
    }
};
